Add support (currently only a reader) for Garmin POI's (.gpi files).
authoroliskoli <oliskoli>
Fri, 18 May 2007 21:28:32 +0000 (21:28 +0000)
committeroliskoli <oliskoli>
Fri, 18 May 2007 21:28:32 +0000 (21:28 +0000)
Makefile.in
garmin_poi.c [new file with mode: 0644]
vecs.c

index be90de4950dc2cd1284c4548a7e0f763e667a222..db70f95fe1503fd60891ef1fbfb50fe3a53e2e8b 100644 (file)
@@ -55,7 +55,7 @@ FMTS=magproto.o gpx.o geo.o mapsend.o mapsource.o garmin_tables.o \
        ignrando.o stmwpp.o msroute.o cst.o nmn4.o mag_pdb.o compegps.o \
        yahoo.o unicsv.o wfff_xml.o garmin_txt.o axim_gpb.o gpssim.o \
        wbt-200.o stmsdf.o gtrnctr.o dmtlog.o raymarine.o alan.o vitovtt.o \
-       ggv_log.o g7towin.o
+       ggv_log.o g7towin.o garmin_poi.o
 
 FILTERS=position.o radius.o duplicate.o arcdist.o polygon.o smplrout.o \
        reverse_route.o sort.o stackfilter.o trackfilter.o discard.o \
@@ -413,6 +413,7 @@ garmin_fs.o: garmin_fs.c defs.h config.h queue.h gbtypes.h zlib/zlib.h \
   jeeps/gpsapp.h jeeps/gpsprot.h jeeps/gpscom.h jeeps/gpsfmt.h \
   jeeps/gpsmath.h jeeps/gpsmem.h jeeps/gpsrqst.h jeeps/gpsinput.h \
   jeeps/gpsproj.h garmin_tables.h
+garmin_poi.o: garmin_poi.c defs.h config.h queue.h gbtypes.h jeeps/gpsmath.h
 garmin_tables.o: garmin_tables.c garmin_tables.h defs.h config.h queue.h \
   gbtypes.h zlib/zlib.h zlib/zconf.h gbfile.h cet.h cet_util.h inifile.h \
   jeeps/gpsmath.h
diff --git a/garmin_poi.c b/garmin_poi.c
new file mode 100644 (file)
index 0000000..567b5cc
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+
+    Support for Garmin Points of Interest (.gpi files)
+    
+    Copyright (C) 2007 Olaf Klein, o.b.klein@gpsbabel.org
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+ */
+/*
+       History:
+
+       * 2007/05/18: initial release (only a reader)
+
+       ToDo:
+       
+       * Display mode ("Symbol & Name")
+       * decode speed/proximity
+       * support category from GMSD "Garmin Special Data"
+       * ... and the writer
+*/
+
+#include "defs.h"
+#include "jeeps/gpsmath.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#define MYNAME "garmin_poi"
+
+#define DEFAULT_ICON   "Waypoint"
+
+static arglist_t garmin_poi_args[] = {
+       ARG_TERMINATOR
+};
+
+typedef struct {
+       char S3[9];             /* "GRMRECnn" */
+       time_t crdate;          /* creation date and time */
+       char POI[4];            /* "POI" */
+       char S8[3];
+       gbint32 codepage;       /* code-page, i.e. 1252 */
+       char *group;
+       char *category;
+} gpi_data_t;
+
+static gbfile *fin;
+static gpi_data_t *dt;
+
+#define GPI_DBG 1
+#undef GPI_DBG
+
+#ifdef GPI_DBG
+# define PP printf("@%1$6x (%1$8d): ", gbftell(fin))
+#else
+# define PP
+#endif
+
+/* read a string with embedded "EN" header */
+static char *
+read_string(const int *sz)
+{
+       char en[3];
+       short slen;
+       char *res;
+       
+       gbfread(&en, 1, 2, fin);
+       
+       en[2] = '\0';
+       if (strcmp(en, "EN") != 0) {
+               fatal(MYNAME  ": Out of sync ('EN' expected)!\n");
+       }
+       
+       slen = gbfgetint16(fin);
+       if (sz && (*sz != slen + 4)) {
+               fatal(MYNAME  ": Out of sync (wrong string size)!\n");
+       }
+       
+       if (slen > 0) {
+               res = xmalloc(slen + 1);
+               res[slen] = '\0';
+               gbfread(res, 1, slen, fin);
+       }
+       else res = NULL;
+       
+       return res;
+}
+
+
+static void
+read_header(void)
+{
+       int len, i;
+#ifdef GPI_DBG
+       struct tm tm;
+       char stime[32];
+#endif 
+
+       i = gbfgetint32(fin);
+       if (i != 0) i = gbfgetint32(fin);
+       (void) gbfgetint32(fin);
+
+       gbfread(&dt->S3, 1, sizeof(dt->S3) - 1, fin);   /* GRMRECnn */
+       if (strncmp(dt->S3, "GRMREC", 6) != 0)
+               fatal(MYNAME ": No GPI file!\n");
+
+       PP;
+       dt->crdate = gbfgetint32(fin);
+#ifdef GPI_DBG
+       tm = *localtime(&dt->crdate);
+       tm.tm_year += 20;       /* !!! */
+       tm.tm_mday -= 1;        /* !!! */
+       strftime(stime, sizeof(stime), "%Y/%m/%d %H:%M:%S", &tm);
+       printf("crdate = %lu (%s)\n", dt->crdate, stime);
+#endif 
+       
+       (void) gbfgetint16(fin);        /* 0 */
+
+       len = gbfgetint16(fin);
+       gbfseek(fin, len, SEEK_CUR);    /* "my.gpi" */
+
+       (void) gbfgetint32(fin);        /* 1 */
+       (void) gbfgetint32(fin);        /* 12 */
+
+       gbfread(&dt->POI, 1, sizeof(dt->POI) - 1, fin);
+       if (strcmp(dt->POI, "POI") != 0)
+               fatal(MYNAME ": Wrong or unsupported GPI file!\n");
+
+       for (i = 0; i < 3; i++) (void)gbfgetc(fin);
+       gbfread(&dt->S8, 1, sizeof(dt->S8) - 1, fin);
+
+       dt->codepage = gbfgetint32(fin);
+
+#ifdef GPI_DBG
+       PP;
+       printf("< leaving header\n");
+#endif 
+}
+
+/* gpi tag handler */
+static int read_tag(const char *caller, const int tag, waypoint *wpt);
+
+
+/* read a single poi with all options */
+static void
+read_poi(const int sz)
+{
+       int pos, len;
+       waypoint *wpt;
+       
+#if GPI_DBG
+       PP;
+       printf("> reading poi (size %d)\n", sz);
+#endif 
+       (void) gbfgetint32(fin);        /* sub-header size */
+       
+       pos = gbftell(fin);
+       
+       wpt = waypt_new();
+       wpt->icon_descr = DEFAULT_ICON;
+       
+       wpt->latitude = GPS_Math_Semi_To_Deg(gbfgetint32(fin));
+       wpt->longitude = GPS_Math_Semi_To_Deg(gbfgetint32(fin));
+       
+       (void) gbfgetint16(fin);        /* ? always 1 ? */
+       (void) gbfgetc(fin);            /* seems to 1 when extra options present */
+       
+       len = gbfgetint32(fin);
+       
+       PP;
+       wpt->shortname = read_string(&len);
+#ifdef GPI_DBG
+       printf("shortname = %s\n", wpt->shortname);
+#endif 
+       
+       while (gbftell(fin) < (pos + sz - 4)) {
+               int tag = gbfgetint32(fin);
+               if (! read_tag("read_poi", tag, wpt)) break;
+       }
+
+       if (wpt->notes && !wpt->description) wpt->description = xstrdup(wpt->notes);
+       if (wpt->description && !wpt->notes) wpt->notes = xstrdup(wpt->description);
+
+       waypt_add(wpt);
+
+#ifdef GPI_DBG
+       PP;
+       printf("< leaving poi\n");
+#endif 
+}
+
+/* read poi's following a group header */
+static void
+read_poi_list(const int sz)
+{
+       int pos;
+       
+#ifdef GPI_DBG
+       PP;
+       printf("> reading poi list\n");
+#endif
+       pos = gbftell(fin);
+       
+       (void) gbfgetint32(fin);        /* mostly 23 (0x17) */
+
+       (void) gbfgetint32(fin);        /* max-lat */
+       (void) gbfgetint32(fin);        /* max-lon */
+       (void) gbfgetint32(fin);        /* min-lat */
+       (void) gbfgetint32(fin);        /* min-lon */
+       
+       (void) gbfgetc(fin);            /* three unknown bytes */
+       (void) gbfgetc(fin);            /* ? should be zero ? */
+       (void) gbfgetc(fin);
+       
+       (void) gbfgetint32(fin);        /* ? const 0x1000100 ? */
+       
+       while (gbftell(fin) < (pos + sz - 4)) {
+               int tag = gbfgetint32(fin);
+               if (! read_tag("read_poi_list", tag, NULL)) return;
+       }
+#ifdef GPI_DBG
+       PP;
+       printf("< leaving poi list\n");
+#endif
+}
+
+
+static void
+read_poi_group(const int sz, const int tag)
+{
+       int len, pos;
+
+#ifdef GPI_DBG
+       PP;
+       printf("> reading poi group\n");
+#endif
+       pos = gbftell(fin);
+       
+       if (tag == 0x80009) {
+               (void) gbfgetint32(fin);        /* ? offset to category data ? */
+       }
+
+       len = gbfgetint32(fin); /* size of group string */
+       PP;
+       if (dt->group) xfree(dt->group);
+       dt->group = read_string(&len);
+#ifdef GPI_DBG
+       printf("Group \"%s\"\n", dt->group);
+#endif
+       while (gbftell(fin) < (pos + sz)) {
+               int subtag = gbfgetint32(fin);
+               if (! read_tag("read_poi_group", subtag, NULL)) break;
+       }
+       
+#ifdef GPI_DBG
+       PP;
+       printf("< leaving poi group\n");
+#endif
+}
+
+
+/* gpi tag handler */
+static int
+read_tag(const char *caller, const int tag, waypoint *wpt)
+{
+       int pos, sz, len;
+       
+       sz = gbfgetint32(fin);
+       pos = gbftell(fin);
+       
+#ifdef GPI_DBG
+       PP;
+       printf("%s: tag = 0x%x (size %d)\n", caller, tag, sz);
+#endif
+       if ((tag >= 0x80000) && (tag <= 0x800ff)) sz += 4;
+       
+       switch(tag) {
+               case 0x3:       /* size = 12 ? sound */
+               case 0x4:       /* size = 2  ? */
+               case 0x6:       /* size = 2  ? */
+                       break;
+                       
+               case 0x5:       /* group bitmap (BMP: 22x22/256) */
+                       break;
+
+               case 0x7:       /* category */
+                       (void) gbfgetint32(fin);
+                       (void) gbfgetint16(fin);
+                       if (dt->category) xfree(dt->category);
+                       PP;
+                       dt->category = read_string(NULL);
+#ifdef GPI_DBG
+                       printf("Category: \"%s\"\n", dt->category);
+#endif
+                       break;
+                       
+               case 0xa:       /* notes */
+                       len = gbfgetint32(fin);
+                       PP;
+                       wpt->notes = read_string(&len);
+#ifdef GPI_DBG
+                       printf("Notes: \"%s\"\n", wpt->notes);
+#endif
+                       break;
+                       
+               case 0xe:       /* ? also notes ? */
+                       len = gbfgetint32(fin);
+                       (void) gbfgetc(fin);
+                       
+                       len = sz - 5;
+                       wpt->description = xmalloc(len + 1);
+                       wpt->description[len] = '\0';
+                       PP;
+                       gbfread(wpt->description, 1, len, fin);
+#ifdef GPI_DBG
+                       printf("Descr: \"%s\"\n", wpt->description);
+#endif
+                       
+                       break;
+
+               case 0x80002:
+                       read_poi(sz);
+                       break;
+
+               case 0x80008:
+                       read_poi_list(sz);
+                       break;
+
+               case 0x9:       /* ? older versions / no category data ? */
+               case 0x80009:   /* current POI loader */
+                       read_poi_group(sz, tag);
+                       break;
+                       
+               case 0x8000b:   /* phone-number */
+                       /* ToDo */
+                       break;
+
+               case 0x8000c:   /* address (street/city...) */
+                       /* ToDo */
+                       break;
+
+               case 0x80012:   /* ? sounds / images ? */
+                       break;
+
+               default:
+                       warning(MYNAME ": Unknown tag (0x%x). Please report!\n", tag);
+                       return 0;
+       }
+       gbfseek(fin, pos + sz, SEEK_SET);
+       return 1;
+}
+
+
+/*******************************************************************************
+* %%%        global callbacks called by gpsbabel main process              %%% *
+*******************************************************************************/
+
+static void
+garmin_poi_rd_init(const char *fname)
+{
+       char cp[8];
+       
+       fin = gbfopen(fname, "r", MYNAME);
+       dt = xcalloc(1, sizeof(*dt));
+
+       read_header();
+       
+       if ((dt->codepage >= 1250) && (dt->codepage <= 1257)) {
+               snprintf(cp, sizeof(cp), "CP%d", dt->codepage);
+               cet_convert_init(cp, 1);
+       }
+       else warning(MYNAME ": Unsupported code page (%d).\n", dt->codepage);
+}
+
+
+static void 
+garmin_poi_rd_deinit(void)
+{
+       if (dt->category) xfree(dt->category);
+       if (dt->group) xfree(dt->group);
+       xfree(dt);
+       gbfclose(fin);
+}
+
+
+static void
+garmin_poi_read(void)
+{
+       while (! gbfeof(fin)) { /* main loop */
+               int tag;
+               
+               tag = gbfgetint32(fin);
+               if ((tag == 0xffff) || (tag == 0xff)) return;
+               
+               if (! read_tag("garmin_poi_read", tag, NULL)) return;
+       };
+}
+
+/**************************************************************************/
+
+ff_vecs_t garmin_poi_vecs = {
+       ff_type_file,
+       { 
+               ff_cap_read | ff_cap_none       /* waypoints */, 
+               ff_cap_none                     /* tracks */, 
+               ff_cap_none                     /* routes */
+       },
+       garmin_poi_rd_init,     
+       NULL,   /* garmin_poi_wr_init, */
+       garmin_poi_rd_deinit,   
+       NULL,   /* garmin_poi_wr_deinit, */
+       garmin_poi_read,
+       NULL,   /* garmin_poi_write, */
+       NULL,
+       garmin_poi_args,
+       CET_CHARSET_MS_ANSI, 0
+};
+
+/**************************************************************************/
diff --git a/vecs.c b/vecs.c
index dbdd99947afd76bff99339c3341581f2d3adca8e..bf3f5fdb9b9f8d3a2d13e78cdf5d3abdcf73862a 100644 (file)
--- a/vecs.c
+++ b/vecs.c
@@ -117,6 +117,7 @@ extern ff_vecs_t alantrl_vecs;
 extern ff_vecs_t vitovtt_vecs;
 extern ff_vecs_t ggv_log_vecs;
 extern ff_vecs_t g7towin_vecs;
+extern ff_vecs_t garmin_poi_vecs;
 
 static
 vecs_t vec_list[] = {
@@ -653,6 +654,12 @@ vecs_t vec_list[] = {
                "g7t"
        },
 #endif
+        {
+                &garmin_poi_vecs,
+                "garmin_poi",
+                "Garmin Points of Interest (.gpi)",
+                "gpi"
+        },
        {
                NULL,
                NULL,